use std::collections::HashMap;
use std::any::{TypeId, Any};
use crate::{Worker, Message, Handler};
use once_cell::sync::Lazy;
use parking_lot::Mutex;
use std::cell::RefCell;
use crate::log;
use crate::channel::Sender;
use crate::worker::envelop::ToEnvelop;

/// 理论上同时间内只有一个Game实例会进行插入，所以锁竞争不会有
/// 如果有多个线程需要读取数据到线程本地时，会出现锁竞争
/// 但是tokio运行时不会创建太多的线程，同时也提供了参数供开发者设置线程参数
/// 不适用RwLock的原因是，Envelop可以轻松证明符合跨线程移动的线程安全，但是不一定能满足跨线程
/// 共享引用的线程安全要求
static SENDER_MAP: Lazy<Mutex<HashMap<TypeId, Box<dyn Any + Send>>>> =
    Lazy::new(|| Mutex::new(HashMap::new()));

pub fn insert_service<W: Worker>(value: Sender<W>) {
    let mut map = SENDER_MAP.lock();
    map.insert(TypeId::of::<W>(), Box::new(value));
}

pub fn get_service<W: Worker>() -> Option<Sender<W>> {
    let map = SENDER_MAP.lock();
    if let Some(service) = map.get(&TypeId::of::<W>()) {
        if let Some(sender) = service.downcast_ref::<Sender<W>>() {
            return Some(sender.clone());
        } else {
            log::error("downcast UnboundedSender error");
        }
    }
    None
}

thread_local! {
    static LOCAL_SENDER_MAP:RefCell<HashMap<TypeId,Box<dyn Any>>>=RefCell::new(HashMap::new());
}

pub fn service_send_msg<W, M>(msg: M)
    where W: Worker + Handler<M>,
          M: Message + Send + 'static,
          W::Context: ToEnvelop<W, M>,
{
    LOCAL_SENDER_MAP.with(|map| {
        let mut borrow = map.borrow_mut();
        let value = borrow.get_mut(&TypeId::of::<W>());
        // 本地存在，则去除sender进行发送
        if let Some(value) = value {
            //　转化为Sender
            if let Some(sender) = value.downcast_ref::<Sender<W>>() {
                sender.send(msg);
            } else {
                log::error("when service send_msg ,Any downcast error");
            }
        } else {
            // 本地缓存没有，从全局容器取
            let sender: Option<Sender<W>> = get_service::<W>();
            if let Some(sender) = sender {
                sender.send(msg);
                borrow.insert(TypeId::of::<W>(), Box::new(sender));
            } else {
                log::error("service not found in global service map");
            }
        }
    });
}

